/**
* Copyright (c) 2006
*      OpenAVS Developers. All Rights Reserved.
*
* Copyright (c) 2005-2006
*      NSCC. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

/**
* ! \file "avsdeclib.c"
*   \brief Interface to AVS Decoding.
*/

#include "define.h"
#include "avsdeclib.h"
#include "global.h"
#include "decode.h"
#include "stream.h"

//////////////////////////////////////////////////////////////////////////
// global variables

STREAMINFO strmInfo;
AVS_BYTE* pbBuf[(INBUFLEN*2) * sizeof(AVS_BYTE)];

VIDEODATA * videoBuf[3];
VIDEODATA * pRefFrame[2];
VIDEODATA * pCurrFrame;
VIDEODATA * videoFieldBuf[5];
VIDEODATA * pRefField[4];
MBINFO * pMbInfo;
BWREFINFO * pBwRefInfo;
// ycb bool firstFrame;
int firstFrame;

int OpenAVSDec(BYTE * pbData, int len, SEQ_INFO * pInfo)
{
	if (DWORD_SWAP(*(AVS_DWORD *) pbData)!= SEQENCE_START_CODE)
		return AVS_FALSE;
	if (len < 20)
		return AVS_NOT_ENOUGH_DATA;
	
	pBwRefInfo = calloc(1, sizeof(BWREFINFO) * MAX_MB_NUM);
	pMbInfo = calloc(1, sizeof(MBINFO) * MAX_MB_NUM);
	firstFrame = 1;
	
	GetMem(videoBuf, 3);
	GetMem(videoFieldBuf, 5);
	
	/* Initialize reference frame buffer */
	pRefFrame[0] = videoBuf[0];
	pRefFrame[1] = videoBuf[1];
	pCurrFrame = videoBuf[2];
	
	InitDecode(pbData, len, &strmInfo);
	
	pInfo->image_width = strmInfo.SeqInfo.dwWidth;
	pInfo->image_height = strmInfo.SeqInfo.dwHeight;
	pInfo->frame_rate = strmInfo.SeqInfo.fFrameRate;
	return 0;
}

int InitAVSDec()
{
	pBwRefInfo = calloc(1, sizeof(BWREFINFO) * MAX_MB_NUM);
	pMbInfo = calloc(1, sizeof(MBINFO) * MAX_MB_NUM);
	firstFrame = 1;
	
	GetMem(videoBuf, 3);
	GetMem(videoFieldBuf, 5);
	
	/* Initialize reference frame buffer */
	pRefFrame[0] = videoBuf[0];
	pRefFrame[1] = videoBuf[1];
	pCurrFrame = videoBuf[2];
	
	MakeVlcTable();
	MakeClipTable();
	MakeTabNoBuf();
	return 0;
}

int GetSeqInfo(BYTE * pbData, int len, SEQ_INFO * pInfo){
	int noUseDataLen = 0;
	int iLeft = len;
	while (pbData[noUseDataLen+0] != 0x00 
		||pbData[noUseDataLen+1] != 0x00 
		||pbData[noUseDataLen+2] != 0x01) {
		noUseDataLen ++;
		if (iLeft < 4)
			return 0;
	}
	SeqenceHeader(pbData + noUseDataLen, len, &(strmInfo.SeqInfo));
	pInfo->image_width = strmInfo.SeqInfo.dwWidth;
	pInfo->image_height = strmInfo.SeqInfo.dwHeight;
	pInfo->frame_rate = strmInfo.SeqInfo.fFrameRate;
	return 1;
}
int GetOneFrameBitsFromBuffer(
							  unsigned char * pFrameBuf,
							  int iBufLen, 
							  int * pFrameLen, 
							  int * pNoUseDataLen
							  )
{
	AVS_BYTE* pbData = pFrameBuf; //< pbData was used in this function.
	AVS_INT iLeft = iBufLen;//< init left length  = Buffer length
	*pFrameLen = 0;	//< frame length
	*pNoUseDataLen = 0;
	while (pbData[(*pNoUseDataLen)+0] != 0x00 
		||pbData[(*pNoUseDataLen)+1] != 0x00 
		||pbData[(*pNoUseDataLen)+2] != 0x01) {
		//	       pbData ++;
		iLeft--;
		(*pNoUseDataLen) ++;
		if (iLeft < 4)
			return 0;
	}
	while (1) {
		switch (pbData[(*pNoUseDataLen)+3]) {
		case 0xB0:	/* Sequence Header */
			if (! FindNextPicOrEndStartCode(pbData + *pNoUseDataLen, iBufLen, (AVS_DWORD *) &iLeft))
				//if (! FindNextStartCode(pbData, iBufLen, (AVS_DWORD *) &iLeft))
				return AVS_NOT_ENOUGH_DATA;
			SeqenceHeader(pbData + *pNoUseDataLen, iBufLen, &(strmInfo.SeqInfo));
			
			*pNoUseDataLen += iBufLen - iLeft;
			break;
		case 0xB5:	/* Extension */
			if (! FindNextPicOrEndStartCode(pbData + *pNoUseDataLen,iBufLen, (AVS_DWORD *) &iLeft))
				//if (! FindNextStartCode(pbData, iBufLen, (AVS_DWORD *) &iLeft))
				return AVS_NOT_ENOUGH_DATA;
			*pNoUseDataLen += iBufLen - iLeft;
			break;
		case 0xB2:	/* User Data */
			if (! FindNextPicOrEndStartCode(pbData + *pNoUseDataLen,  iBufLen, (AVS_DWORD *) &iLeft))
				//if (! FindNextStartCode(pbData, iBufLen, (AVS_DWORD *) &iLeft))
				return AVS_NOT_ENOUGH_DATA;
			*pNoUseDataLen = iBufLen - iLeft;
			break;
		case 0xB3:	/* I frame */
			if (! FindNextPicOrEndStartCode(pbData + *pNoUseDataLen, 
				iBufLen, 
				(AVS_DWORD *) &iLeft))
				return AVS_NOT_ENOUGH_DATA;
			*pFrameLen = iBufLen-iLeft + *pNoUseDataLen;
			//			*pNoUseDataLen = iBufLen - iLeft;
			return 1;
		case 0xB6:	/* PB frame */
			if (! FindNextPicOrEndStartCode(pbData + *pNoUseDataLen, 
				iBufLen, 
				(AVS_DWORD *) &iLeft))
				return AVS_NOT_ENOUGH_DATA;
			*pFrameLen = iBufLen - iLeft + *pNoUseDataLen;
			//			*pNoUseDataLen = iBufLen - iLeft;
			return 1;
		case 0xB1:	/* Sequence End */
			return 0;
		default:
			if(! FindNextStartCode(pbData + *pNoUseDataLen, iBufLen, &iLeft))
				return AVS_NOT_ENOUGH_DATA;
			*pNoUseDataLen += iBufLen - iLeft;
			break;
			
		}
		/* Adjust buffter length */
		iBufLen = iLeft;
	}
}

int DecOneFrameFromBuffer(
						  unsigned char *pbData, //!< data to be decoded
						  int iFrameLen,  //!< frame length
						  BYTE** ppOutY, BYTE** ppOutU, BYTE** ppOutV //!<out put
						  )
{
#if PRINTINFO
	int ii = 0;
	printf("Frame first 16 byte : ");
	for(ii = 0; ii < 15; ii++){
		printf("%x ", pbData[ii]);
	}
	printf("\n");
#endif
	if ((DWORD_SWAP(*(AVS_DWORD *) pbData) == I_FRAME_START_CODE) || 
		(DWORD_SWAP(*(AVS_DWORD*)pbData) == PB_FRAME_START_CODE)) {
#if PRINTINFO
		printf("Start Decode This Frame\n");
#endif
		
		DecodeOneFrame(pbData, iFrameLen, &strmInfo, 
			pRefFrame, &pCurrFrame, pMbInfo, pBwRefInfo);
	}
	if (firstFrame) {
#if PRINTINFO
		printf("This is the first frame, Init ref Frame, *ppOutYUV = pRefFrame[0]->YUV\n");
#endif
		*ppOutY = pRefFrame[0]->y;
		*ppOutU = pRefFrame[0]->u;
		*ppOutV = pRefFrame[0]->v; 
		firstFrame = FALSE;
	}
	else if ((strmInfo.ImgInfo.dwImageType != B_IMG)) {
#if PRINTINFO
		printf("This is a I/P frame, *ppOutYUV = pRefFrame[1]->YUV\n");
#endif
		*ppOutY = pRefFrame[1]->y;
		*ppOutU = pRefFrame[1]->u;
		*ppOutV = pRefFrame[1]->v; 
	}
	else {
#if PRINTINFO
		printf("This is a B frame, *ppOutYUV = pCurrFrame->YUV\n");
#endif
		*ppOutY = pCurrFrame->y;
		*ppOutU = pCurrFrame->u;
		*ppOutV = pCurrFrame->v; 
	}
	
	return AVS_NOERROR;
}

void CloseAVSDec()
{
	free(pBwRefInfo);
	free(pMbInfo);
	ReleaseMem(videoBuf, 3);
	ReleaseMem(videoFieldBuf, 5);
}
